const JsonKit = Java.type('io.zbus.data.kit.JsonKit');
const JsKit = Java.type('io.zbus.data.kit.JsKit');
const InvocationContext = Java.type('io.zbus.rpc.InvocationContext');
const JsFeature = Java.type('io.zbus.data.server.js.JsFeature'); 

function Map2Obj(map) {
	if (map == null) {
		return {};
	}
	const str = JsonKit.toJSONString(map);
	const object = JSON.parse(str);
	return object;
}
function Obj2Map(obj) {
	return JsKit.convert(obj);
}
  
class Response {} 
/**
 * 构建Context对象
 * @param javaContext Java Context, 在Spring XML中配置
 * @param globalFunctions 所有已被调用过的函数集合
 * @param javascriptInvoker 函数调用器
 * @param urlPrefix url前缀
 * @returns
 */
function $buildContext(javaContext, globalFunctions, javascriptInvoker, urlPrefix) {
    const newContext = {};
    // 这个 next 给function用
    const nextForInvoke = (jsReq, jsRes) => {
        const url = urlPrefix+'/'+jsReq.url;
        jsReq.setUrl(url);
        const localCachedFunc = globalFunctions.get(url);
        if (localCachedFunc) {
        	// call local function
            return localCachedFunc(jsReq, jsRes, nextForInvoke);
        } else {
			// call remote function
			const currJavaReq = InvocationContext.getRequest();
			const currJavaRes = InvocationContext.getResponse();
            return javascriptInvoker.invoke(currJavaReq, currJavaRes);
        }
    };
    newContext['next'] = nextForInvoke;
    const funcProxy = (targetFunc, javaReq, javaRes, jsCtx, self, jsFilter, jsFilterConfig, uri) => {
    	const jsReq = {};
    	const jsRes = new Response();
    	const ctx = {
			...jsCtx,
			request: jsReq,
			response: jsRes,
			self
    	};
    	// req
    	jsReq.url = javaReq.getUrl();
    	jsReq.setUrl = url => {
    		jsReq.url = url;
    		javaReq.setUrl(url);
    		return jsReq;
    	}
    	jsReq.method = javaReq.getMethod();
    	jsReq.setMethod = method => {
    		jsReq.method = method;
    		javaReq.setMethod(method);
    		return jsReq;
    	};
    	jsReq.queryString = javaReq.getQueryString();
		jsReq.query = jsReq.params = Map2Obj(javaReq.getParams());
		
    	jsReq.setQuery = (key, value) => {
    		jsReq.query[key] = value;
    		javaReq.setParam(key, value);
    		return jsReq;
		};
		jsReq.appendQuery = (key, value) => {
			const { query } = jsReq;
			const vals = query[key] || [];
			vals.push(value);
			query[key] = vals;
			javaReq.setParam(key, vals);
    		return jsReq;
    	};
		jsReq.headers = Map2Obj(javaReq.getHeaders()); // TODO Immutable
    	jsReq.setHeader = (key, value) => {
    		jsReq.headers[key] = value;
    		javaReq.setHeader(key, value);
    		return jsReq;
    	}
    	jsReq.cookies = Map2Obj(javaReq.getCookies()); // TODO Immutable
    	jsReq.setCookie = (key, value) => {
    		jsReq.cookies[key] = value;
    		javaReq.setCookie(key, value);// TODO setRequestCookie
    		return jsReq;
    	}
    	// res
    	jsRes.status = javaRes.getStatus(); 
    	jsRes.setStatus = (status) => {
    		javaRes.setStatus(status);
    		jsRes.status = status;
    		return jsRes;
    	}
    	jsRes.setHeader = (key, value) => {
    		javaRes.setHeader(key, value);
    		const { headers={} } = jsRes;
    		headers[key] = value;
    		jsRes.headers = headers;
    		return jsRes;
    	}
    	jsRes.setCookie = (key, value) => {
    		javaRes.setCookie(key, value);// TODO setResponseCookie
    		const { cookies={} } = jsRes;
	    	cookies[key] = value;
	    	jsRes.cookies = cookies;
    		return jsRes;
    	}
    	jsRes.setBody = (body) => {
    		const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
    		javaRes.setBody(bodyStr);
    		jsRes.body = body;
    		return jsRes;
    	} 
    	
    	let useFilter = true;
    	if (jsFilter != null) { // TODO /*, /**
			const { include=null, exclude=null } = jsFilterConfig;
			if(include && include.indexOf(uri) == -1) {
				useFilter = false;
			} 
            if (exclude && exclude.indexOf(uri) !== -1) {
            	useFilter = false;
            }
        }
    	const next = () => {
    		return targetFunc(ctx, nextForInvoke);
    	};
    	let result;
    	if (useFilter) {
    		result = jsFilter(ctx, next);
        } else {
        	result = next();
        }
    	if (result && result instanceof Response) {
    		return javaRes;
    	}
    	return result;
    };
    newContext['funcProxy'] = funcProxy;
    
    javaContext.keySet().forEach((key) => {
        const val = javaContext[key];
        let newVal = val
        if (val instanceof JsFeature) {
        	newVal = {}
            val.methods().forEach((m) => {
            	newVal[m] = (...args) => {
                    const result = val.callMethod(m, args); 
                    const jsResult = JSON.parse(result);
                    return jsResult
                }
            });
        }
        newContext[key] = newVal;
    });
    
    return newContext;
}